
#include <android/log.h>
#include <android/native_window_jni.h>
#include <android/native_window.h>
#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"DEBUG",FORMAT,##__VA_ARGS__)
#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"DEBUG",FORMAT,##__VA_ARGS__)

#ifdef __cplusplus
extern "C" {

#include <pthread.h>
#include <malloc.h>
#include <string.h>
#include "queue.h"
#include "x264/x264.h"
#include "rtmpdump/rtmp.h"
#include "faac/faac.h"

#ifndef TRUE
#define TRUE	1
#define FALSE	0
#endif

#define CONNECT_FAILED 101
#define INIT_FAILED 102

//x264编码输入图像YUV420P
x264_picture_t pic_in;
x264_picture_t pic_out;
//YUV个数
int y_len, u_len, v_len;
//x264编码处理器
x264_t *video_encode_handle;

unsigned int start_time;
//线程处理
pthread_mutex_t mutex;
pthread_cond_t cond;
//rtmp流媒体地址
char *rtmp_path;
//是否直播
int is_pushing = FALSE;
//faac音频编码处理器
faacEncHandle audio_encode_handle;

unsigned long nInputSamples; //输入的采样个数
unsigned long nMaxOutputBytes; //编码输出之后的字节数

jobject jobj_push_native; //Global ref
jclass jcls_push_native;
jmethodID jmid_throw_native_error;
JavaVM *javaVM;

#endif

void add_rtmp_packet(RTMPPacket *packet);
/**
 * 添加AAC头信息
 */
void add_aac_sequence_header(){
	//获取aac头信息的长度
	unsigned char *buf;
	unsigned long len; //长度
	faacEncGetDecoderSpecificInfo(audio_encode_handle,&buf,&len);
	int body_size = 2 + len;
	RTMPPacket *packet = (RTMPPacket *)malloc(sizeof(RTMPPacket));
	//RTMPPacket初始化
	RTMPPacket_Alloc(packet,body_size);
	RTMPPacket_Reset(packet);
	 char * body = packet->m_body;
	//头信息配置
	/*AF 00 + AAC RAW data*/
	body[0] = 0xAF;//10 5 SoundFormat(4bits):10=AAC,SoundRate(2bits):3=44kHz,SoundSize(1bit):1=16-bit samples,SoundType(1bit):1=Stereo sound
	body[1] = 0x00;//AACPacketType:0表示AAC sequence header
	memcpy(&body[2], buf, len); /*spec_buf是AAC sequence header数据*/
	packet->m_packetType = RTMP_PACKET_TYPE_AUDIO;
	packet->m_nBodySize = body_size;
	packet->m_nChannel = 0x04;
	packet->m_hasAbsTimestamp = 0;
	packet->m_nTimeStamp = 0;
	packet->m_headerType = RTMP_PACKET_SIZE_MEDIUM;
	add_rtmp_packet(packet);
	free(buf);

}

/**
 * 添加AAC rtmp packet
 */
void add_aac_body(unsigned char *buf, int len){
	int body_size = 2 + len;
	RTMPPacket *packet = (RTMPPacket *)malloc(sizeof(RTMPPacket));
	//RTMPPacket初始化
	RTMPPacket_Alloc(packet,body_size);
	RTMPPacket_Reset(packet);
	 char * body = packet->m_body;
	//头信息配置
	/*AF 00 + AAC RAW data*/
	body[0] = 0xAF;//10 5 SoundFormat(4bits):10=AAC,SoundRate(2bits):3=44kHz,SoundSize(1bit):1=16-bit samples,SoundType(1bit):1=Stereo sound
	body[1] = 0x01;//AACPacketType:1表示AAC raw
	memcpy(&body[2], buf, len); /*spec_buf是AAC raw数据*/
	packet->m_packetType = RTMP_PACKET_TYPE_AUDIO;
	packet->m_nBodySize = body_size;
	packet->m_nChannel = 0x04;
	packet->m_hasAbsTimestamp = 0;
	packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
	packet->m_nTimeStamp = RTMP_GetTime() - start_time;
	add_rtmp_packet(packet);
}

//获取JavaVM
jint JNI_OnLoad(JavaVM* vm, void* reserved){
	javaVM = vm;
	return JNI_VERSION_1_4;
}

/**
 * 向Java层发送错误信息
 */
void throwNativeError(JNIEnv *env,int code){
	env->CallVoidMethod(jobj_push_native,jmid_throw_native_error,code);
}

/**
 * 从队列中不断拉取RTMPPacket发送给流媒体服务器）
 */
void *push_thread(void * arg){
	JNIEnv* env;//获取当前线程JNIEnv
	javaVM->AttachCurrentThread(&env,NULL);
	//建立RTMP连接
	RTMP *rtmp = RTMP_Alloc();
	if(!rtmp){
	 	LOGE("rtmp初始化失败");
		goto end;
	}
	RTMP_Init(rtmp);
	rtmp->Link.timeout = 5; //连接超时的时间
	//设置流媒体地址
	RTMP_SetupURL(rtmp,rtmp_path);
	//发布rtmp数据流
	RTMP_EnableWrite(rtmp);
	//建立连接
	if(!RTMP_Connect(rtmp,NULL)){
	 	LOGE("%s","RTMP 连接失败");
		throwNativeError(env,CONNECT_FAILED);
		goto end;
	}
	//计时
	start_time = RTMP_GetTime();
	if(!RTMP_ConnectStream(rtmp,0)){ //连接流
	 	LOGE("%s","RTMP ConnectStream failed");
		throwNativeError(env,CONNECT_FAILED);
		goto end;
	}
	is_pushing = TRUE;
	//发送AAC头信息
	add_aac_sequence_header();

	while(is_pushing){
		//发送
		pthread_mutex_lock(&mutex);
		pthread_cond_wait(&cond,&mutex);
		//取出队列中的RTMPPacket
		RTMPPacket *packet = (RTMPPacket *)queue_get_first();
		if(packet){
			queue_delete_first(); //移除
			packet->m_nInfoField2 = rtmp->m_stream_id; //RTMP协议，stream_id数据
            LOGE("rtmp->%p packet->%p",rtmp,packet);
            int i= RTMP_SendPacket(rtmp,packet,TRUE); //TRUE放入librtmp队列中，并不是立即发送
            LOGE("RTMP_SendPacket->%d",i);
			if(!i){
                if (!RTMP_IsConnected(rtmp)){
                    LOGE("RTMP 断开");
                    RTMPPacket_Free(packet);
                    pthread_mutex_unlock(&mutex);
                    goto end;
                } else{
                    LOGE("RTMP 未知状态");
                }

			}else{
			 	LOGI("%s","rtmp send packet");
			}
			RTMPPacket_Free(packet);
		}
		pthread_mutex_unlock(&mutex);
	}
	end:
 	LOGI("%s","释放资源");
	free(rtmp_path);
	RTMP_Close(rtmp);
	RTMP_Free(rtmp);
	javaVM->DetachCurrentThread();
	return 0;
}

JNIEXPORT void JNICALL Java_com_szsfm_ffmpegtest_live_PushNative_startPush
		(JNIEnv *env, jobject jobj, jstring url_jstr){
	//jobj(PushNative对象)
	jobj_push_native = env->NewGlobalRef(jobj);

	jclass jcls_push_native_tmp = env->GetObjectClass(jobj);
	//TODO jcls_push_native = env->NewGlobalRef(jcls_push_native_tmp);
	if(jcls_push_native_tmp == NULL){
	 	LOGI("%s","NULL");
	}else{
		 LOGI("%s","not NULL");
	}
	//PushNative.throwNativeError
	jmid_throw_native_error = env->GetMethodID(jcls_push_native_tmp,"throwNativeError","(I)V");

	//初始化的操作
	const char* url_cstr = env->GetStringUTFChars(url_jstr,NULL);
	//复制url_cstr内容到rtmp_path
	rtmp_path = (char *)malloc(strlen(url_cstr) + 1);
	memset(rtmp_path,0,strlen(url_cstr) + 1);
	memcpy(rtmp_path,url_cstr,strlen(url_cstr));

	//初始化互斥锁与条件变量
	pthread_mutex_init(&mutex,NULL);
	pthread_cond_init(&cond,NULL);

	//创建队列
	create_queue();
	//启动消费者线程（从队列中不断拉取RTMPPacket发送给流媒体服务器）
	pthread_t push_thread_id;
	pthread_create(&push_thread_id, NULL,push_thread, NULL);

	env->ReleaseStringUTFChars(url_jstr,url_cstr);
}


JNIEXPORT void JNICALL Java_com_szsfm_ffmpegtest_live_PushNative_stopPush
		(JNIEnv *env, jobject jobj){
	is_pushing = FALSE;
}


JNIEXPORT void JNICALL Java_com_szsfm_ffmpegtest_live_PushNative_release
		(JNIEnv *env, jobject jobj){
	env->DeleteGlobalRef(jcls_push_native);
	env->DeleteGlobalRef(jobj_push_native);
	//env->DeleteGlobalRef(jmid_throw_native_error);
}

/**
 * 设置视频参数
 */
JNIEXPORT void JNICALL Java_com_szsfm_ffmpegtest_live_PushNative_setVideoOptions
		(JNIEnv *env, jobject jobj, jint width, jint height, jint bitrate, jint fps){
	x264_param_t param;
	//x264_param_default_preset 设置
	x264_param_default_preset(&param,"ultrafast","zerolatency");
	//编码输入的像素格式YUV420P
	param.i_csp = X264_CSP_I420;
	param.i_width  = width;
	param.i_height = height;

	y_len = width * height;
	u_len = y_len / 4;
	v_len = u_len;

	//参数i_rc_method表示码率控制，CQP(恒定质量)，CRF(恒定码率)，ABR(平均码率)
	//恒定码率，会尽量控制在固定码率
	param.rc.i_rc_method = X264_RC_CRF;
	param.rc.i_bitrate = bitrate / 1000; //* 码率(比特率,单位Kbps)
	param.rc.i_vbv_max_bitrate = bitrate / 1000 * 1.2; //瞬时最大码率

	//码率控制不通过timebase和timestamp，而是fps
	param.b_vfr_input = 0;
	param.i_fps_num = fps; //* 帧率分子
	param.i_fps_den = 1; //* 帧率分母
	param.i_timebase_den = param.i_fps_num;
	param.i_timebase_num = param.i_fps_den;
	param.i_threads = 1;//并行编码线程数量，0默认为多线程

	//是否把SPS和PPS放入每一个关键帧
	//SPS Sequence Parameter Set 序列参数集，PPS Picture Parameter Set 图像参数集
	//为了提高图像的纠错能力
	param.b_repeat_headers = 1;
	//设置Level级别
	param.i_level_idc = 51;
	//设置Profile档次
	//baseline级别，没有B帧
	x264_param_apply_profile(&param,"baseline");

	//x264_picture_t（输入图像）初始化
	x264_picture_alloc(&pic_in, param.i_csp, param.i_width, param.i_height);
	pic_in.i_pts = 0;
	//打开编码器
	video_encode_handle = x264_encoder_open(&param);
	if(video_encode_handle){
	 	LOGI("打开视频编码器成功");
	}else{
		throwNativeError(env,INIT_FAILED);
	}
}

/**
 * 音频编码器配置
 */
JNIEXPORT void JNICALL Java_com_szsfm_ffmpegtest_live_PushNative_setAudioOptions
		(JNIEnv *env, jobject jobj, jint sampleRateInHz, jint numChannels){
	audio_encode_handle = faacEncOpen(sampleRateInHz,numChannels,&nInputSamples,&nMaxOutputBytes);
	if(!audio_encode_handle){
		 LOGE("音频编码器打开失败");
		return;
	}
	//设置音频编码参数
	faacEncConfigurationPtr p_config = faacEncGetCurrentConfiguration(audio_encode_handle);
	p_config->mpegVersion = MPEG4;
	p_config->allowMidside = 1;
	p_config->aacObjectType = LOW;
	p_config->outputFormat = 0; //输出是否包含ADTS头
	p_config->useTns = 1; //时域噪音控制,大概就是消爆音
	p_config->useLfe = 0;
//	p_config->inputFormat = FAAC_INPUT_16BIT;
	p_config->quantqual = 100;
	p_config->bandWidth = 0; //频宽
	p_config->shortctl = SHORTCTL_NORMAL;

	if(!faacEncSetConfiguration(audio_encode_handle,p_config)){
	 	LOGE("%s","音频编码器配置失败..");
		throwNativeError(env,INIT_FAILED);
		return;
	}

	 LOGI("%s","音频编码器配置成功");
}

/**
 * 加入RTMPPacket队列，等待发送线程发送
 */
void add_rtmp_packet(RTMPPacket *packet){
	pthread_mutex_lock(&mutex);
	if(is_pushing){
		queue_append_last(packet);
	}
	pthread_cond_signal(&cond);
	pthread_mutex_unlock(&mutex);
}


/**
 * 发送h264 SPS与PPS参数集
 */
void add_264_sequence_header(unsigned char* pps,unsigned char* sps,int pps_len,int sps_len){
	int body_size = 16 + sps_len + pps_len; //按照H264标准配置SPS和PPS，共使用了16字节
	RTMPPacket *packet = (RTMPPacket *)malloc(sizeof(RTMPPacket));
	//RTMPPacket初始化
	RTMPPacket_Alloc(packet,body_size);
	RTMPPacket_Reset(packet);

    char * body = packet->m_body;
	int i = 0;
	//二进制表示：00010111
	body[i++] = 0x17;//VideoHeaderTag:FrameType(1=key frame)+CodecID(7=AVC)
	body[i++] = 0x00;//AVCPacketType = 0表示设置AVCDecoderConfigurationRecord
	//composition time 0x000000 24bit ?
	body[i++] = 0x00;
	body[i++] = 0x00;
	body[i++] = 0x00;

	/*AVCDecoderConfigurationRecord*/
	body[i++] = 0x01;//configurationVersion，版本为1
	body[i++] = sps[1];//AVCProfileIndication
	body[i++] = sps[2];//profile_compatibility
	body[i++] = sps[3];//AVCLevelIndication
	//?
	body[i++] = 0xFF;//lengthSizeMinusOne,H264 视频中 NALU的长度，计算方法是 1 + (lengthSizeMinusOne & 3),实际测试时发现总为FF，计算结果为4.

	/*sps*/
	body[i++] = 0xE1;//numOfSequenceParameterSets:SPS的个数，计算方法是 numOfSequenceParameterSets & 0x1F,实际测试时发现总为E1，计算结果为1.
	body[i++] = (sps_len >> 8) & 0xff;//sequenceParameterSetLength:SPS的长度
	body[i++] = sps_len & 0xff;//sequenceParameterSetNALUnits
	memcpy(&body[i], sps, sps_len);
	i += sps_len;

	/*pps*/
	body[i++] = 0x01;//numOfPictureParameterSets:PPS 的个数,计算方法是 numOfPictureParameterSets & 0x1F,实际测试时发现总为E1，计算结果为1.
	body[i++] = (pps_len >> 8) & 0xff;//pictureParameterSetLength:PPS的长度
	body[i++] = (pps_len) & 0xff;//PPS
	memcpy(&body[i], pps, pps_len);
	i += pps_len;

	//Message Type，RTMP_PACKET_TYPE_VIDEO：0x09
	packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;
	//Payload Length
	packet->m_nBodySize = body_size;
	//Time Stamp：4字节
	//记录了每一个tag相对于第一个tag（File Header）的相对时间。
	//以毫秒为单位。而File Header的time stamp永远为0。
	packet->m_nTimeStamp = 0;
	packet->m_hasAbsTimestamp = 0;
	packet->m_nChannel = 0x04; //Channel ID，Audio和Vidio通道
	packet->m_headerType = RTMP_PACKET_SIZE_MEDIUM; //?
	//将RTMPPacket加入队列
	add_rtmp_packet(packet);

}

/**
 * 发送h264帧信息
 */
void add_264_body(unsigned char *buf ,int len){
	//去掉起始码(界定符)
	if(buf[2] == 0x00){  //00 00 00 01
		buf += 4;
		len -= 4;
	}else if(buf[2] == 0x01){ // 00 00 01
		buf += 3;
		len -= 3;
	}
	int body_size = len + 9;
	RTMPPacket *packet = (RTMPPacket *)malloc(sizeof(RTMPPacket));
	RTMPPacket_Alloc(packet,body_size);

	char * body = packet->m_body;
	//当NAL头信息中，type（5位）等于5，说明这是关键帧NAL单元
	//buf[0] NAL Header与运算，获取type，根据type判断关键帧和普通帧
	//00000101 & 00011111(0x1f) = 00000101
	int type = buf[0] & 0x1f;
	//Inter Frame 帧间压缩
	body[0] = 0x27;//VideoHeaderTag:FrameType(2=Inter Frame)+CodecID(7=AVC)
	//IDR I帧图像
	if (type == NAL_SLICE_IDR) {
		body[0] = 0x17;//VideoHeaderTag:FrameType(1=key frame)+CodecID(7=AVC)
	}
	//AVCPacketType = 1
	body[1] = 0x01; /*nal unit,NALUs（AVCPacketType == 1)*/
	body[2] = 0x00; //composition time 0x000000 24bit
	body[3] = 0x00;
	body[4] = 0x00;

	//写入NALU信息，右移8位，一个字节的读取？
	body[5] = (len >> 24) & 0xff;
	body[6] = (len >> 16) & 0xff;
	body[7] = (len >> 8) & 0xff;
	body[8] = (len) & 0xff;

	/*copy data*/
	memcpy(&body[9], buf, len);

	packet->m_hasAbsTimestamp = 0;
	packet->m_nBodySize = body_size;
	packet->m_packetType = RTMP_PACKET_TYPE_VIDEO;//当前packet的类型：Video
	packet->m_nChannel = 0x04;
	packet->m_headerType = RTMP_PACKET_SIZE_LARGE;
//	packet->m_nTimeStamp = -1;
	packet->m_nTimeStamp = RTMP_GetTime() - start_time;//记录了每一个tag相对于第一个tag（File Header）的相对时间
	add_rtmp_packet(packet);

}


/**
 * 将采集到视频数据进行编码
 */
JNIEXPORT void JNICALL Java_com_szsfm_ffmpegtest_live_PushNative_fireVideo
		(JNIEnv *env, jobject jobj, jbyteArray buffer){
	//视频数据转为YUV420P
	//NV21->YUV420P
	jbyte* nv21_buffer = env->GetByteArrayElements(buffer,NULL);
	jbyte* u = (jbyte*)pic_in.img.plane[1];
	jbyte* v = (jbyte*)pic_in.img.plane[2];
	//nv21 4:2:0 Formats, 12 Bits per Pixel
	//nv21与yuv420p，y个数一致，uv位置对调
	//nv21转yuv420p  y = w*h,u/v=w*h/4
	//nv21 = yvu yuv420p=yuv y=y u=y+1+1 v=y+1
	memcpy(pic_in.img.plane[0], nv21_buffer, y_len);
	int i;
	for (i = 0; i < u_len; i++) {
		*(u + i) = *(nv21_buffer + y_len + i * 2 + 1);
		*(v + i) = *(nv21_buffer + y_len + i * 2);
	}

	//h264编码得到NALU数组
	x264_nal_t *nal = NULL; //NAL
	int n_nal = -1; //NALU的个数
	//进行h264编码
	if(x264_encoder_encode(video_encode_handle,&nal, &n_nal,&pic_in,&pic_out) < 0){
		 LOGE("%s","编码失败");
		return;
	}
	//使用rtmp协议将h264编码的视频数据发送给流媒体服务器
	//帧分为关键帧和普通帧，为了提高画面的纠错率，关键帧应包含SPS和PPS数据
	int sps_len , pps_len;
	unsigned char sps[100];
	unsigned char pps[100];
	memset(sps,0,100);
	memset(pps,0,100);
	pic_in.i_pts += 1; //顺序累加
	//遍历NALU数组，根据NALU的类型判断
	for(i=0; i < n_nal; i++){
		if(nal[i].i_type == NAL_SPS){
			//复制SPS数据
			sps_len = nal[i].i_payload - 4;
			memcpy(sps,nal[i].p_payload + 4,sps_len); //不复制四字节起始码
		}else if(nal[i].i_type == NAL_PPS){
			//复制PPS数据
			pps_len = nal[i].i_payload - 4;
			memcpy(pps,nal[i].p_payload + 4,pps_len); //不复制四字节起始码

			//发送序列信息
			//h264关键帧会包含SPS和PPS数据
			add_264_sequence_header(pps,sps,pps_len,sps_len);

		}else{
			//发送帧信息
			add_264_body(nal[i].p_payload,nal[i].i_payload);
		}

	}

	env->ReleaseByteArrayElements(buffer,nv21_buffer,NULL);
}

/**
 * 对音频采样数据进行AAC编码
 */
JNIEXPORT void JNICALL Java_com_szsfm_ffmpegtest_live_PushNative_fireAudio
		(JNIEnv *env, jobject jobj, jbyteArray buffer, jint len){
	int *pcmbuf;
	unsigned char *bitbuf;
	jbyte* b_buffer = env->GetByteArrayElements(buffer, 0);
	pcmbuf = (int*) malloc(nInputSamples * sizeof(int));
	bitbuf = (unsigned char*) malloc(nMaxOutputBytes * sizeof(unsigned char));
	int nByteCount = 0;
	unsigned int nBufferSize = (unsigned int) len / 2;
	unsigned short* buf = (unsigned short*) b_buffer;
	while (nByteCount < nBufferSize) {
		int audioLength = nInputSamples;
		if ((nByteCount + nInputSamples) >= nBufferSize) {
			audioLength = nBufferSize - nByteCount;
		}
		int i;
		for (i = 0; i < audioLength; i++) {//每次从实时的pcm音频队列中读出量化位数为8的pcm数据。
			int s = ((int16_t *) buf + nByteCount)[i];
			pcmbuf[i] = s << 8;//用8个二进制位来表示一个采样量化点（模数转换）
		}
		nByteCount += nInputSamples;
		//利用FAAC进行编码，pcmbuf为转换后的pcm流数据，audioLength为调用faacEncOpen时得到的输入采样数，bitbuf为编码后的数据buff，nMaxOutputBytes为调用faacEncOpen时得到的最大输出字节数
		int byteslen = faacEncEncode(audio_encode_handle, pcmbuf, audioLength,
									 bitbuf, nMaxOutputBytes);
		if (byteslen < 1) {
			continue;
		}
		add_aac_body(bitbuf, byteslen);//从bitbuf中得到编码后的aac数据流，放到数据队列
	}
	env->ReleaseByteArrayElements(buffer, b_buffer, NULL);
	if (bitbuf)
		free(bitbuf);
	if (pcmbuf)
		free(pcmbuf);
}

#ifdef __cplusplus
}
#endif